package com.lhl.wx.cp.autoconfigure;

import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Maps;
import com.lhl.wx.cp.handler.*;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxRuntimeException;
import me.chanjar.weixin.common.redis.JedisWxRedisOps;
import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
import me.chanjar.weixin.common.redis.WxRedisOps;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
import me.chanjar.weixin.cp.config.impl.WxCpRedissonConfigImpl;
import me.chanjar.weixin.cp.constant.WxCpConsts;
import me.chanjar.weixin.cp.message.WxCpMessageInterceptor;
import me.chanjar.weixin.cp.message.WxCpMessageRouter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @className: com.lhl.wx.cp.autoconfigure.WxCpConfiguration
 * @description: 企业号多账号封装
 * @author: king
 * @date: 2020-12-09 15:59
 **/
@Slf4j
@Configuration
@ConditionalOnClass(WxCpService.class)
@EnableConfigurationProperties(WxCpProperties.class)
@ConditionalOnProperty(prefix = "wx.cp", value = "enabled", matchIfMissing = true)
public class WxCpConfiguration {
    private final WxCpProperties wxCpProperties;
    private final ApplicationEventPublisher applicationEventPublisher;
    private final ApplicationContext applicationContext;

    private static Map<Integer, WxCpMessageRouter> routers = Maps.newHashMap();
    private static Map<Integer, WxCpService> cpServices = Maps.newHashMap();

    @Autowired
    public WxCpConfiguration(WxCpProperties wxCpProperties,ApplicationContext applicationContext,ApplicationEventPublisher applicationEventPublisher) {
        this.wxCpProperties = wxCpProperties;
        this.applicationContext = applicationContext;
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public static Map<Integer, WxCpMessageRouter> getRouters() {
        return routers;
    }

    public static WxCpService getCpService(Integer agentId) {
        return cpServices.get(agentId);
    }

    public static WxCpService getCpService() {
        return cpServices.get(0);
    }

    public static WxCpMessageRouter getRouter(Integer agentId) {
        return routers.get(agentId);
    }

    @PostConstruct
    public void initServices() {
        List<WxCpProperties.AppConfig> appConfigs = this.wxCpProperties.getAppConfigs();
        if (CollUtil.isEmpty(appConfigs)) {
            throw new WxRuntimeException("大哥，拜托先看下项目首页的说明（readme文件），添加下相关配置，注意别配错了！");
        }
        cpServices = appConfigs.stream().map(a -> {
//            val configStorage = new WxCpDefaultConfigImpl();
            WxCpDefaultConfigImpl configStorage = wxCpConfigStorage("wx:cp:"+a.getAgentId());
            configStorage.setCorpId(StringUtils.trimToNull(this.wxCpProperties.getCorpId()));
            configStorage.setAgentId(a.getAgentId());
            configStorage.setCorpSecret(StringUtils.trimToNull(a.getSecret()));
            configStorage.setToken(StringUtils.trimToNull(a.getToken()));
            configStorage.setAesKey(StringUtils.trimToNull(a.getAesKey()));
            WxCpServiceImpl service = new WxCpServiceImpl();
            service.setWxCpConfigStorage(configStorage);
            routers.put(a.getAgentId(), this.newRouter(service));
            return service;
        }).collect(Collectors.toMap(service -> service.getWxCpConfigStorage().getAgentId(), a -> a));
        //添加新的cpServices
        for (Map.Entry<Integer, String> entry : this.wxCpProperties.getCorpSecrets().entrySet()) {
            WxCpDefaultConfigImpl configStorage = wxCpConfigStorage("wx:cp:"+entry.getKey());
            configStorage.setCorpId(this.wxCpProperties.getCorpId());
            configStorage.setCorpSecret(entry.getValue());
            WxCpServiceImpl service = new WxCpServiceImpl();
            service.setWxCpConfigStorage(configStorage);
            routers.put(entry.getKey(), this.newRouter(service));
            cpServices.put(entry.getKey(),service);
        }
    }

    private WxCpMessageRouter newRouter(WxCpService wxCpService) {
        final WxCpMessageRouter newRouter = new WxCpMessageRouter(wxCpService);
        //记录所有事件的日志 （异步执行）
        newRouter.rule().handler(new LogHandler()).next();
        // 自定义菜单事件
        newRouter.rule().async(false).interceptor(wxCpMessageInterceptor()).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.MenuButtonType.CLICK).content("自定义菜单事件").handler(new MenuHandler()).end();
        // 点击菜单链接事件（这里使用了一个空的处理器，可以根据自己需要进行扩展）
        newRouter.rule().async(false).interceptor(wxCpMessageInterceptor()).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.MenuButtonType.VIEW).content("点击菜单链接事件").handler(new NullHandler()).end();
        // 关注事件
        newRouter.rule().async(false).interceptor(wxCpMessageInterceptor()).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.SUBSCRIBE).content("关注事件").handler(new SubscribeHandler()).end();
        // 取消关注事件
        newRouter.rule().async(false).interceptor(wxCpMessageInterceptor()).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.UNSUBSCRIBE).content("取消关注事件").handler(new UnsubscribeHandler()).end();
        // 上报地理位置事件
        newRouter.rule().async(false).interceptor(wxCpMessageInterceptor()).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.LOCATION).content("上报地理位置事件").handler(new LocationHandler()).end();
        // 接收地理位置消息
        newRouter.rule().async(false).interceptor(wxCpMessageInterceptor()).msgType(WxConsts.XmlMsgType.LOCATION).content("接收地理位置消息")
                .handler(new LocationHandler()).end();
        // 扫码事件（这里使用了一个空的处理器，可以根据自己需要进行扩展）
        newRouter.rule().async(false).interceptor(wxCpMessageInterceptor()).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.SCAN).content("扫码事件").handler(new ScanHandler()).end();
        // 通讯录变更事件处理器
        newRouter.rule().async(false).interceptor(wxCpMessageInterceptor()).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxCpConsts.EventType.CHANGE_CONTACT).handler(new ContactChangeHandler()).end();
        //
        newRouter.rule().async(false).interceptor(wxCpMessageInterceptor()).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxCpConsts.EventType.ENTER_AGENT).handler(new EnterAgentHandler()).end();
        // 默认
        newRouter.rule().async(false).interceptor(wxCpMessageInterceptor()).handler(new MsgHandler()).end();
        return newRouter;
    }

    @Bean
    public WxCpMessageInterceptor wxCpMessageInterceptor(){
        return (wxCpXmlMessage,context,wxCpService,wxSessionManager)->{
            context.put("king","22222222222");
            context.put("event",applicationEventPublisher);
            return true;
        };
    }

    private WxCpDefaultConfigImpl wxCpConfigStorage(String keyPrefix) {
        WxCpDefaultConfigImpl configStorage;
        switch (wxCpProperties.getConfigStorage().getType()) {
            case Jedis:
                configStorage = wxCpJedisConfigStorage(keyPrefix);
                break;
            case RedisTemplate:
                configStorage = wxCpRedisTemplateConfigStorage(keyPrefix);
                break;
            default:
                configStorage = wxCpDefaultConfigStorage();
                break;
        }

        WxCpProperties.ConfigStorage configStorageProperties = wxCpProperties.getConfigStorage();
        configStorage.setHttpProxyHost(configStorageProperties.getHttpProxyHost());
        configStorage.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername());
        configStorage.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword());
        if (configStorageProperties.getHttpProxyPort() != null) {
            configStorage.setHttpProxyPort(configStorageProperties.getHttpProxyPort());
        }
        return configStorage;
    }

    private WxCpDefaultConfigImpl wxCpDefaultConfigStorage() {
        return new WxCpDefaultConfigImpl();
    }

    private WxCpDefaultConfigImpl wxCpJedisConfigStorage(String keyPrefix) {
        WxCpProperties.RedisProperties redisProperties = wxCpProperties.getConfigStorage().getRedis();
        JedisPool jedisPool;
        if (StringUtils.isNotEmpty(redisProperties.getHost())) {
            JedisPoolConfig config = new JedisPoolConfig();
            if (redisProperties.getMaxActive() != null) {
                config.setMaxTotal(redisProperties.getMaxActive());
            }
            if (redisProperties.getMaxIdle() != null) {
                config.setMaxIdle(redisProperties.getMaxIdle());
            }
            if (redisProperties.getMaxWaitMillis() != null) {
                config.setMaxWaitMillis(redisProperties.getMaxWaitMillis());
            }
            if (redisProperties.getMinIdle() != null) {
                config.setMinIdle(redisProperties.getMinIdle());
            }
            config.setTestOnBorrow(true);
            config.setTestWhileIdle(true);

            jedisPool = new JedisPool(config, redisProperties.getHost(), redisProperties.getPort(),
                    redisProperties.getTimeout(), redisProperties.getPassword(), redisProperties.getDatabase());
        } else {
            jedisPool = applicationContext.getBean(JedisPool.class);
        }
        WxRedisOps redisOps = new JedisWxRedisOps(jedisPool);
        return new WxCpRedissonConfigImpl(redisOps, keyPrefix);
    }
    private WxCpDefaultConfigImpl wxCpRedisTemplateConfigStorage(String keyPrefix) {
        StringRedisTemplate redisTemplate = applicationContext.getBean(StringRedisTemplate.class);
        WxRedisOps redisOps = new RedisTemplateWxRedisOps(redisTemplate);
        return new WxCpRedissonConfigImpl(redisOps, keyPrefix);
    }
}
